﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using game_for_test_helper.src.helper;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using game_for_test_helper.src.util;
using SkinnedModel;

namespace game_for_test_helper.src.graphics
{
    class ModelCreator //BAK
    {
        private String name;
        private Dictionary<string, AnimationClip> clipdictionary = null;

        private Model model;
        AnimationPlayer animationPlayer;
        SkinningData skinningData;
        BoundingSphere[] SphereArray;
        public ModelCreator(String setName)
        {
            name = setName;
            clipdictionary = new Dictionary<string, AnimationClip>();
            ModelTransform = Matrix.Identity;
            Load(setName);
        }

        private void Load(String AssetName)
        {
            model = GameStaticHolder.ContentHolder1.Load<Model>(AssetName);
            skinningData = model.Tag as SkinningData;

            SphereArray = new BoundingSphere[model.Meshes.Count];
            for (int i=0;i<SphereArray.Length;i++)
            {
                SphereArray[i] = new BoundingSphere(model.Meshes[i].BoundingSphere.Center,model.Meshes[i].BoundingSphere.Radius);
            }

            if (skinningData == null)
                throw new InvalidOperationException("This model does not contain a SkinningData tag.");
            animationPlayer = new AnimationPlayer(skinningData);
            PraseClip();

        }
        private const int Head = 1;
        private const int End = 2;
        private int SerchKeyPosition(Keyframe[] keyf, TimeSpan timesp, int HeadOrEnd)
        {
            int dest = 0, mid, l = 0, r = keyf.Length - 1;
            while (r - l > 0)
            {
                mid = (l + r) / 2;
                int com = keyf[mid].Time.CompareTo(timesp);
                if (com == 0)
                {
                    dest = mid;
                    break;
                }
                else if (com > 0)
                    r = mid - 1;
                else if (com < 0)
                    l = mid + 1;
                dest = (l + r) / 2;
            }
            if (HeadOrEnd == Head)
            {
                l = dest + 1;
                while (true)
                {
                    l--;
                    if (l < 0 || !keyf[l].Time.Equals(keyf[dest].Time))
                        break;
                }
                dest = l + 1;
            }
            else
            {
                r = dest - 1;
                while (true)
                {
                    r++;
                    if (r == keyf.Length || !keyf[r].Time.Equals(keyf[dest].Time))
                        break;
                }
                dest = r - 1;
            }
            return dest;
        }
        private TakeConfigure tcf;
        public game_for_test_helper.src.graphics.TakeConfigure TakeConfigurer
        {
            get { return tcf; }
            set { tcf = value; }
        }
        private void PraseClip()
        {
            tcf = null;
            PlayingClip = false;
            PlayingClipLoopForever = false;
            PlayingCurrentClip1 = null;

            string xmlname = name + ".xml";
            string path = GameStaticHolder.ContentHolder1.RootDirectory + "/" + xmlname;
            if (System.IO.File.Exists(path))
                tcf = (TakeConfigure)SerializeUtil.DeSerilizeAnFileToObject(path, typeof(TakeConfigure));
            if (tcf != null && tcf.getTakesCount() > 0)
            {
                IList<Keyframe> keylist = skinningData.AnimationClips["Take 001"].Keyframes;
                Keyframe[] keyframes = keylist.ToArray();
                TimeSpan during = skinningData.AnimationClips["Take 001"].Duration;

                for (int index = 0; index < tcf.getTakesCount(); index++)
                {
                    IList<Keyframe> tempkeyframelist = new List<Keyframe>();
                    TimeSpan beginTime = new TimeSpan((long)(tcf.getBeginFloatPercent(index) * during.Ticks));
                    TimeSpan endTime = new TimeSpan((long)(tcf.getEndFloatPercent(index) * during.Ticks));
                    int beginkey = SerchKeyPosition(keyframes, beginTime, Head);
                    int endkey = SerchKeyPosition(keyframes, endTime, End);
                    TimeSpan cuttime = keyframes[beginkey].Time;
                    for (int index2 = beginkey; index2 <= endkey; index2++)
                    {
                        Keyframe newkey = new Keyframe(keyframes[index2].Bone, keyframes[index2].Time.Subtract(cuttime), keyframes[index2].Transform);
                        tempkeyframelist.Add(newkey);
                    }
                    TimeSpan duration = new TimeSpan(endTime.Ticks - beginTime.Ticks);
                    AnimationClip clip = new AnimationClip(duration, tempkeyframelist);
                    clipdictionary.Add(tcf.getTake(index).Clipname, clip);
                }
            }

            AnimationClip clip1 = skinningData.AnimationClips["Take 001"];
            IList<Keyframe> tempkeyframelist2 = new List<Keyframe>();
            int pos = 0;
            while (clip1.Keyframes[pos].Time.Ticks < 100)
                pos++;
            for (int i = 0; i < pos; i++)
            {
                Keyframe newkey2 = new Keyframe(clip1.Keyframes[i].Bone, clip1.Keyframes[i].Time, clip1.Keyframes[i].Transform);
                tempkeyframelist2.Add(newkey2);
            }
            AnimationClip clip2 = new AnimationClip(new TimeSpan(1), tempkeyframelist2);
            animationPlayer.StartClip(clip2);
            animationPlayer.Update(new TimeSpan(1), true, Matrix.Identity);
            initBones = animationPlayer.GetSkinTransforms();
            modelInit();
        }
        private bool PlayingClip;
        private bool PlayingClipLoopForever;
        private AnimationClip PlayingCurrentClip;

        public AnimationClip PlayingCurrentClip1
        {
            get { return PlayingCurrentClip; }
            set { PlayingCurrentClip = value; }
        }
        private TimeSpan PlayingTimeRest;
        public void doAction(string takesname, bool loopendless)
        {
            PlayingClip = true;
            PlayingClipLoopForever = loopendless;
            PlayingCurrentClip1 = clipdictionary[takesname];
            PlayingTimeRest = PlayingCurrentClip1.Duration;
            animationPlayer.StartClip(PlayingCurrentClip1);
        }
        public void modelInit()
        {
            PlayingClip = false;
            PlayingClipLoopForever = false;
            PlayingCurrentClip1 = null;
            bones = initBones;
        }
        public void update(GameTime gameTime)
        {
            if (PlayingClip)
            {
                animationPlayer.Update(gameTime.ElapsedGameTime, true, Matrix.Identity);
                if (PlayingClipLoopForever == false)
                {
                    PlayingTimeRest = PlayingTimeRest.Subtract(gameTime.ElapsedGameTime);
                    if (PlayingTimeRest.Ticks <= 0) PlayingClip = false;
                }
            }
        }
        #region SphereCollistion
        public bool WheatherCollisionScene(Vector3 Position)
        {
            for (int i = 0; i < SphereArray.Length;i++ )
            {
                BoundingSphere bs = SphereArray[i].Transform(Matrix.CreateTranslation(Position));
                if (SceneLoader.GlobalScene.Intersect(bs))
                    return true;
            }
            return false;
        }


        #endregion
        private Matrix ModelTransform;
        private Matrix[] initBones;
        private Matrix[] bones;
        public void Render(Matrix renderMatrix)
        {
            ModelTransform = renderMatrix;
            GraphicsDevice my_device = GameStaticHolder.GraphicsDevice1;
            my_device.VertexDeclaration = new VertexDeclaration(my_device, VertexPositionNormalTexture.VertexElements);
            if (model != null)
            {
                if (PlayingClip)
                {
                    bones = animationPlayer.GetSkinTransforms();
                }

                foreach (ModelMesh mesh in model.Meshes)
                {
                    foreach (Effect effect in mesh.Effects)
                    {
                        effect.Parameters["World"].SetValue(renderMatrix);
                        effect.Parameters["Bones"].SetValue(bones);
                        effect.Parameters["View"].SetValue(SimpleCamera.ViewMatrix);
                        effect.Parameters["Projection"].SetValue(GameStaticHolder.ProjectionMatrix);
                    }
                    mesh.Draw();
                }
            }
        }
        public Dictionary<string, AnimationClip> Clipdictionary
        {
            get { return clipdictionary; }
            set { clipdictionary = value; }
        }

    }
    public class TakeConfigure
    {
        private List<ModelTakesDefine> takelist;
        private long totalKeyInTake001;
        public int getTakesCount()
        {
            return takelist.Count;
        }
        public float getBeginFloatPercent(int index)
        {
            return takelist[index].BeginToFloat() / totalKeyInTake001;
        }
        public float getEndFloatPercent(int index)
        {
            return takelist[index].EndToFloat() / totalKeyInTake001;
        }
        public ModelTakesDefine getTake(int index)
        {
            return takelist[index];
        }
        public List<ModelTakesDefine> Takelist
        {
            get { return takelist; }
            set { takelist = value; }
        }
        public long TotalKeyInTake001
        {
            get { return totalKeyInTake001; }
            set { totalKeyInTake001 = value; }
        }
    }

    public class ModelTakesDefine
    {
        string clipname;
        int begin;
        int end;
        public int Lenght()
        {
            return end - begin + 1;
        }
        public string Clipname
        {
            get { return clipname; }
            set { clipname = value; }
        }

        public int Begin
        {
            get { return begin; }
            set { begin = value; }
        }
        public int End
        {
            get { return end; }
            set { end = value; }
        }
        public float BeginToFloat()
        {
            return (float)begin;
        }
        public float EndToFloat()
        {
            return (float)end;
        }
    }
}
